home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware Grab Bag
/
Shareware Grab Bag.iso
/
007
/
lpmac1.arc
/
MACROS.SRC
< prev
next >
Wrap
Text File
|
1987-03-03
|
30KB
|
1,044 lines
;;
;; This is file: MACROS.MLB.
;;
;; It contains the source for a macro library.
;; Written by Mark Hersey.
;;
;; Comments by Lew Paper, 2/16/87
;;
;; Print macro expansion control by Lew Paper
;; Default is .XALL, which prints code only
;; Change by macro variable MACRO_EXPANSION_CONTROL
;; 0 for .XALL, which prints code only
;; 1 for .SALL, which surpresses all printing
;; 2 for .LALL, which prints both code and comments
.XCREF SET_MACRO_EXPANSION
SET_MACRO_EXPANSION MACRO
IFNDEF MACRO_EXPANSION_CONTROL
.XALL
ENDIF ;; IFNDEF MACRO_EXPANSION_CONTROL
IFDEF MACRO_EXPANSION_CONTROL
IF MACRO_EXPANSION_CONTROL EQ 1
.SALL
ELSE
IF MACRO_EXPANSION_CONTROL EQ 2
.LALL
ENDIF ;; IF MACRO_EXPANSION_CONTROL EQ 2
ENDIF ;; IF MACRO_EXPANSION_CONTROL EQ 1
ENDIF ;; IFDEF MACRO_EXPANSION_CONTROL
ENDM ;; SET_MACRO_EXPANDSION
;;
;; I8086 symbols repeated by LP
IFNDEF TRUE
TRUE EQU 001H ;Used to indicate true condition.
ENDIF ;IFNDEF TRUE
IFNDEF ZFLAG
ZFLAG EQU 00040H
ENDIF ;IFNDEF ZFLAG
;;
;; Everything from here to the end of macro POP_REGISTERS builds
;; the macro pair PUSH_REGISTERS and POP_REGISTERS. PUSH_REGISTERS
;; pushes a list of registers and builds a stack for POP_REGISTERS
;; to restore them from.
;; LP
;;
;; Convert a REGISTER to a SERIAL position in LIST
;; Set SERIAL to 0 if no match
.XCREF REGISTER_TO_SERIAL_FOR_LIST
REGISTER_TO_SERIAL_FOR_LIST MACRO REGISTER, SERIAL, LIST
.XCREF SERIAL_LIST_ITEM
SERIAL_LIST_ITEM = 0 ;; Initialize
SERIAL = 0 ;; Assume no match
IRP REGISTER_LIST_ITEM, <LIST>
SERIAL_LIST_ITEM = SERIAL_LIST_ITEM + 1
;; Serial number for this list item
IFIDN <REGISTER>, <REGISTER_LIST_ITEM>
SERIAL = SERIAL_LIST_ITEM
EXITM
ENDIF ;; IFIDN
ENDM ;; IRP REGISTER_LIST_ITEM, <Upper case>
ENDM ;; REGISTER_TO_SERIAL_FOR_LIST
;;
;; Convert a REGISTER to a SERIAL position in the standard register list
;; Set SERIAL to 0 if no match
.XCREF REGISTER_TO_SERIAL
REGISTER_TO_SERIAL MACRO REGISTER, SERIAL
REGISTER_TO_SERIAL_FOR_LIST REGISTER, SERIAL, <AX,BX,CX,DX,SI,DI,SP,BP,DS,ES,SS>
IFE SERIAL ;; No upper case match
REGISTER_TO_SERIAL_FOR_LIST REGISTER, SERIAL, <ax,bx,cx,dx,si,di,sp,bp,ds,es,ss>
ENDIF ;; IFE SERIAL
ENDM ;; REGISTER_TO_SERIAL
;;
;; POP a register which matches SERIAL in the standard register list
;; If unable to convert, enter
;; "ERROR, BAD SPEC FOR SERIAL_TO_POP_REGISTER" as a syntax error
.XCREF SERIAL_TO_POP_REGISTER
SERIAL_TO_POP_REGISTER MACRO SERIAL
.XCREF UNMATCHED_SERIAL_TO_POP_REGISTER, SERIAL_LIST_ITEM
SERIAL_LIST_ITEM = 0 ;; Initialize
UNMATCHED_SERIAL_TO_POP_REGISTER = 1
;; Assume no match
IRP REGISTER_LIST_ITEM, <AX,BX,CX,DX,SI,DI,SP,BP,DS,ES,SS>
SERIAL_LIST_ITEM = SERIAL_LIST_ITEM + 1
IF SERIAL EQ SERIAL_LIST_ITEM
POP REGISTER_LIST_ITEM
UNMATCHED_SERIAL_TO_POP_REGISTER = 0
;; Show match
EXITM
ENDIF ;; IF SERIAL EQ SERIAL_LIST_ITEM
ENDM ;; IRP REGISTER_LIST_ITEM, <...>
IF UNMATCHED_SERIAL_TO_POP_REGISTER
ERROR, BAD SPEC FOR SERIAL_TO_POP_REGISTER
ENDIF ;; IF UNMATCHED_SERIAL_TO_POP_REGISTER
ENDM ;; SERIAL_TO_POP_REGISTER
;;
.XCREF PHASE_1_REGISTER_STACK_INIT, PHASE_2_REGISTER_STACK_INIT
PHASE_1_REGISTER_STACK_INIT = 1
PHASE_2_REGISTER_STACK_INIT = 1
;;
;; Initial the register stack if necessary
.XCREF REGISTER_STACK_INIT_IF_NECESSARY
REGISTER_STACK_INIT_IF_NECESSARY MACRO
.XCREF REGISTER_STACK_TOP
IF1
IF PHASE_1_REGISTER_STACK_INIT ;; Not initialized
REGISTER_STACK_TOP = 0
PHASE_1_REGISTER_STACK_INIT = 0
ENDIF ;; IF PHASE_1_REGISTER_STACK_INIT
ENDIF ;; IF1
IF2
IF PHASE_2_REGISTER_STACK_INIT ;; Not re-initialized
REGISTER_STACK_TOP = 0
PHASE_2_REGISTER_STACK_INIT = 0
ENDIF ;; IF PHASE_2_REGISTER_STACK_INIT
ENDIF ;; IF2
ENDM ;; REGISTER_STACK_INIT_IF_NECESSARY
;;
;; Save VALUE in REGISTER_STACK(RS_TOP). RS_TOP is always
;; REGISTER_STACK_TOP, so this is really the end of PUSH_REGISTER_STACK
;; which is necessary to create the name of REGISTER_STACK_TOP, because
;; MASM only evaluates arguments.
.XCREF SAVE_REGISTER_STACK
SAVE_REGISTER_STACK MACRO RS_TOP, VALUE
.XCREF REGISTER_STACK&RS_TOP
REGISTER_STACK&RS_TOP = VALUE
ENDM ;; SAVE_REGISTER_STACK
;;
;; Push REGISTER_STACK_VALUE on the register stack
;; Initialize the register stack if necessary.
.XCREF PUSH_REGISTER_STACK
PUSH_REGISTER_STACK MACRO REGISTER_STACK_VALUE
REGISTER_STACK_INIT_IF_NECESSARY
REGISTER_STACK_TOP = REGISTER_STACK_TOP + 1
SAVE_REGISTER_STACK %REGISTER_STACK_TOP, REGISTER_STACK_VALUE
ENDM ;; PUSH_REGISTER_STACK
;;
;; PUSH REGISTR
;; Push the serial register index for REGISTR on the register
;; stack and return it in REGISTER_SERIAL.
;; If an error, enter
;; "ERROR, UNABLE TO PUSH REGISTER " and the register name as a
;; syntax error and set REGISTER_SERIAL to 0.
.XCREF PUSH_REGISTER_SERIAL
PUSH_REGISTER_SERIAL MACRO REGISTR, REGISTER_SERIAL
REGISTER_TO_SERIAL REGISTR, REGISTER_SERIAL
;; Find serial for standard register
;; list
IF REGISTER_SERIAL ;; No error
PUSH REGISTR
PUSH_REGISTER_STACK %REGISTER_SERIAL
ELSE ;; Error
ERROR, UNABLE TO PUSH REGISTER REGISTR
;; Error message as syntax error
ENDIF ;; IF REGISTER_SERIAL
ENDM ;; PUSH_REGISTER_SERIAL
;;
;; POP the register whose serial number is in
;; REGISTER_STACK(RS_TOP). RS_TOP is always REGISTER_STACK_TOP, so
;; this is really an intermediate step of POP_REGISTER_STACK, which
;; is necessary to create the name of the variable and to get its
;; value, because MASM only evaluate arguments.
.XCREF REGISTER_STACK_TO_POP_REGISTER
REGISTER_STACK_TO_POP_REGISTER MACRO RS_TOP
SERIAL_TO_POP_REGISTER %(REGISTER_STACK&RS_TOP)
ENDM ;; REGISTER_STACK_TO_POP_REGISTER
;;
;; POP the register whose serial number is at the top of the
;; register stack.
;; Pop the register stack
;; If the register stack is empty, enter
;; "ERROR, UNABLE TO POP THE EMPTY REGISTER STACK" as a syntax error
.XCREF POP_REGISTER_STACK
POP_REGISTER_STACK MACRO
IF REGISTER_STACK_TOP ;; Not empty
REGISTER_STACK_TO_POP_REGISTER %REGISTER_STACK_TOP
REGISTER_STACK_TOP = REGISTER_STACK_TOP - 1
;; Pop the register stack
ELSE ;; Empty
ERROR, UNABLE TO POP THE EMPTY REGISTER STACK
ENDIF ;; IF REGISTER_STACK_TOP
ENDM ;; POP_REGISTER_STACK
;;
.XCREF PUSH_REGISTERS,POP_REGISTERS
;;
;; Push the contents of a list of registers on the program's stack.
;; Also set up a register stack in the macro language so that
;; POP_REGISTERS will pop them in the proper reversed order.
PUSH_REGISTERS MACRO REGS
;; REGS is the list of registers. The < and > are requred.
;; Separate the registers with commas.
.XCREF REGISTER_STACK_COUNT, REGISTER_SERIAL
REGISTER_STACK_COUNT = 0 ;; Initialize. Will contain a
;; count of the registers pushed.
;; At the end, will be the top of
;; the register stack
IRP REG,<REGS>
PUSH_REGISTER_SERIAL REG, REGISTER_SERIAL
;; PUSH register REG on the program's
;; stack. Push the serial number for
;; its name on the register stack
IF REGISTER_SERIAL ;; No errors
REGISTER_STACK_COUNT = REGISTER_STACK_COUNT + 1
ENDIF ;; IF REGISTER_SERIAL
ENDM ;; IRP REG,<REGS>
PUSH_REGISTER_STACK %REGISTER_STACK_COUNT
ENDM ;; PUSH_REGISTERS
;;
;; Pop the register stack to REGISTER_STACK_COUNT. RS_TOP is
;; always REGISTER_STACK_TOP. It is necessary because MASM only
;; evaluates arguments
.XCREF POP_REGISTER_STACK_COUNT
POP_REGISTER_STACK_COUNT MACRO RS_TOP
REGISTER_STACK_COUNT = REGISTER_STACK&RS_TOP
REGISTER_STACK_TOP = REGISTER_STACK_TOP - 1
ENDM ;; POP_REGISTER_STACK_COUNT
;;
;; Pop the program's stack to the registers which PUSH_REGISTERS saved
;; If there is nothing saved by PUSH_REGISTERS, enter
;; "ERROR, NO REGISTERS LEFT TO POP" as a syntax error.
POP_REGISTERS MACRO
REGISTER_STACK_INIT_IF_NECESSARY
IF REGISTER_STACK_TOP ;; PUSH_REGISTERS has been run
;; and not cleared
POP_REGISTER_STACK_COUNT %REGISTER_STACK_TOP
;; Pop the register stack to
;; REGISTER_STACK_COUNT
IF REGISTER_STACK_COUNT ;; Some registers saved
REPT REGISTER_STACK_COUNT
POP_REGISTER_STACK ;; POP the proper register and pop
;; the register stack
ENDM ;; REPT REGISTER_STACK_COUNT
ENDIF ;; IF REGISTER_STACK_COUNT
ELSE ;; PUSH_REGISTERS has not been run or has been
;; cleared
ERROR, NO REGISTERS LEFT TO POP
ENDIF ;; IF REGISTER_STACK_TOP
ENDM ;; POP_REGISTERS
;;
;;
;; This ends the PUSH_REGISTERS - POP_REGISTERS material. LP
;;
.XCREF PUSHM,POPM
;;
PUSHM MACRO REGS
IRP REG,<REGS>
PUSH REG
ENDM
ENDM
;;
POPM MACRO REGS
IRP REG,<REGS>
POP REG
ENDM
ENDM
;;
.XCREF GETZ,GETNZ,NOTZ
;;
;; Set AL to TRUE if the ZERO flag is set. Set it to FALSE
;; if the ZERO flag is reset. Don't change the ZERO flag.
GETZ MACRO
LOCAL SKIPZ
MOV AL,TRUE
JZ SKIPZ
XOR AL,AL
SKIPZ:
AND AL,AL
ENDM
;; Set AL to TRUE if the ZERO flag is reset. Set it to FALSE
;; if the ZERO flag is set. Don't change the ZERO flag.
;;
GETNZ MACRO
LOCAL SKIPNZ
MOV AL,TRUE
JNZ SKIPNZ
XOR AL,AL
SKIPNZ:
ENDM
;;
;; Reverse the ZERO flag. As a side effect, set AH to 40H if
;; the zero flag was set and to 0 if it was not.
NOTZ MACRO
LAHF
AND AH,ZFLAG
ENDM
;;
.XCREF JNNC,JNNZ,JNNNZ,JNNE,JNNNE,JNNS,JU,JNU,JNNCXZ
;; All of the JNN? macros remove the "NN" which other macro
;; substitutions can insert. JU is an unconditional jump, and JNU
;; is a NOP, which is the negation of an unconditional jump.
JNNC MACRO LABEL
JC LABEL
ENDM
;;
JNNZ MACRO LABEL
JZ LABEL
ENDM
;;
JNNNZ MACRO LABEL
JNZ LABEL
ENDM
;;
JNNE MACRO LABEL
JE LABEL
ENDM
;;
JNNNE MACRO LABEL
JNE LABEL
ENDM
;;
JNNS MACRO LABEL
JS LABEL
ENDM
;;
JU MACRO LABEL
JMP SHORT LABEL
ENDM
;;
JNU MACRO LABEL ;No code: "Jump never"!
ENDM
;;
JNNCXZ MACRO LABEL
JCXZ LABEL
ENDM
;;
.XCREF JNNB,JNNA
JNNB MACRO LABEL
JB LABEL
ENDM
;;
JNNA MACRO LABEL
JA LABEL
ENDM
;;
;;
;; Additional macros to eliminate the effects of JNN? by Lew
;; Paper
.XCREF JNNBE, JNNLE, JNNL, JNNGE, JNNG, JNNO, JNNP, JNPO, JNPE
;;
JNNBE MACRO LABEL
JBE LABEL
ENDM ;; JNNBE
;;
JNNLE MACRO LABEL
JLE LABEL
ENDM ;; JNNLE
;;
JNNL MACRO LABEL
JL LABEL
ENDM ;; JNNL
;;
JNNGE MACRO LABEL
JGE LABEL
ENDM ;; JNNGE
;;
JNNG MACRO LABEL
JG LABEL
ENDM ;; JNNG
JNNO MACRO LABEL
JO LABEL
ENDM ;; JNNO
;;
JNNP MACRO LABEL
JP LABEL
ENDM ;; JNNP
;;
JNPO MACRO LABEL
JPE LABEL
ENDM ;; JNPO
;;
JNPE MACRO LABEL
JPO LABEL
ENDM ;; JNPE
;;
;;
;; The macros in this section are by Lew Paper to automate
;; the choices between LONG and SHORT backward jumps and to comment
;; unncessary LONG forward jumps.
;;
;;
;; Sets SHORT_LABEL to 1 if BACK_LABEL is within 126 bytes of $
;; or to 0 if it is move than 126 bytes away. Does not check that
;; BACK_LABEL precedes $.
.XCREF SET_SHORT_BACK
SET_SHORT_BACK MACRO BACK_LABEL
.XCREF SHORT_LABEL
SHORT_LABEL = $ - OFFSET BACK_LABEL LE 126
ENDM ;; SET_SHORT_BACK
;;
;; Sets FORWARD_SHORT_LABEL to 1 if FORWARD_LABEL is within
;; MAX_BYTES bytes of $ or to 0 if it is move than MAX_BYTES bytes away.
;; MAX_BYTES is 127 bytes maximum for all SHORT jumps plus 3 for the
;; size of a LONG jump which is added to a short jump around it for
;; conditional jumps. Does not check that FORWARD_LABEL follows $.
.XCREF SET_SHORT_FORWARD
SET_SHORT_FORWARD MACRO FORWARD_LABEL, MAX_BYTES
.XCREF FORWARD_SHORT_LABEL
IF1 ;; Must define macro variables in
;; pass 1
FORWARD_SHORT_LABEL = 0 ;; Value is insignificant
ENDIF ;; IF1
IF2 ;; FOWARD_LABEL is only defined on
;; pass 2
FORWARD_SHORT_LABEL = OFFSET FORWARD_LABEL - $ LE MAX_BYTES
ENDIF ;; IF2
ENDM ;; SET_SHORT_FORWARD
;;
IFDEF LP_DEBUG
.XCREF D_SHORT_LABEL
D_SHORT_LABEL MACRO
.LALL
IF SHORT_LABEL
; SHORT_LABEL is true
ELSE
; SHORT_LABEL is false
ENDIF ;; IF SHORT_LABEL
SET_MACRO_EXPANSION
ENDM ;; D_SHORT_LABEL
ENDIF ;; IFDEF LP_DEBUG
;;
;; Print a comment that a LONG jump to FORWARD_LABEL is not
;; necessary if it isn't. Set UNNECESSARY_LONG_COMMENTS to 1. See
;; SET_SHORT_FORWARD for a description of MAX_BYTES.
.XCREF IS_LONG_NECESSARY
IS_LONG_NECESSARY MACRO FORWARD_LABEL, MAX_BYTES
.XCREF UNNECESSARY_LONG_COMMENTS
SET_SHORT_FORWARD FORWARD_LABEL, MAX_BYTES
;; Set foward_short_label to TRUE
;; if LONG is not necessary
IF1 ;; Macro variables must be defined
;; in pass 1
UNNECESSARY_LONG_COMMENTS = 0
;; Value doesn't matter
ENDIF ;; IF1
IF FORWARD_SHORT_LABEL ;; Can only be TRUE on pass 2
.LALL ;; List this comment
;****** This jump doesn't need to be LONG ******
.SALL
SET_MACRO_EXPANSION
UNNECESSARY_LONG_COMMENTS = 1
ENDIF ;; IF SHORT_LABEL
ENDM ;; IS_LONG_NECESSARY
;;
;; Write a warning of unncessary long comments
.XCREF OUT_UNNECESSARY_LONG_COMMENTS
OUT_UNNECESSARY_LONG_COMMENTS MACRO
IFDEF UNNECESSARY_LONG_COMMENTS
IF UNNECESSARY_LONG_COMMENTS
;; Can only be positive on phase 2
.LALL
;
; There were some LONG jumps which could have been SHORT.
;
.SALL
%OUT *
%OUT * There were some LONG jumps which could have been SHORT.
%OUT *
ENDIF ;; IF UNNECESSARY_LONG_COMMENTS
ENDIF ;; IFDEF UNNECESSARY_LONG_COMMENTS
ENDM ;; OUT_UNNECESSARY_LONG_COMMENTS
;;
;; Set BACKWARD_JUMP to 1 if a jump to TARGET_LABEL is
;; backward or 0 if it is forward.
.XCREF SET_BACKWARD_JUMP
SET_BACKWARD_JUMP MACRO TARGET_LABEL
.XCREF BACKWARD_JUMP
IF1
IFNDEF TARGET_LABEL ;; Must be forward jump
BACKWARD_JUMP = 0
ENDIF ;; IFNDEF TARGET_LABEL
IFDEF TARGET_LABEL ;; Must be backward jump
BACKWARD_JUMP = 1
ENDIF ;; IFDEF TARGET_LABEL
ENDIF ;; IF1
IF2
BACKWARD_JUMP = $ - OFFSET TARGET_LABEL GT 0
ENDIF ;; IF2
ENDM ;; SET_BACKWARD_JUMP
;;
;; Unconditional jump to TARGET_LABEL. Automatically
;; determine whether backward jump is LONG or SHORT. Set some
;; value in LONG to force a forward long jump. If a forward
;; jump is LONG but could be SHORT, write a remark to that effect
;; and set UNNECESSARY_LONG_COMMENTS to 1.
.XCREF LJMP
LJMP MACRO TARGET_LABEL, LONG
LOCAL BYPASS
SET_BACKWARD_JUMP TARGET_LABEL ;; 1 if backward, 0 if forward
IF BACKWARD_JUMP
SET_SHORT_BACK TARGET_LABEL ;; SHORT_LABEL is TRUE if SHORT
ELSE ;; Forward
.XCREF SHORT_LABEL
SHORT_LABEL = 1 ;; Assume SHORT
IFNB <LONG> ;; LONG is not blank
.XCREF LJMP_MAX_SHORT
LJMP_MAX_SHORT = 127 + OFFSET BYPASS - $
IS_LONG_NECESSARY TARGET_LABEL, LJMP_MAX_SHORT
;; Write comment for unnecessary LONG
SHORT_LABEL = 0
ENDIF ;; IFNB
ENDIF ;; IF BACKWARD_JUMP
IF SHORT_LABEL
JMP SHORT TARGET_LABEL
ELSE ;; LONG
JMP TARGET_LABEL
ENDIF ;; If short label
IFE BACKWARD_JUMP ;; Forward jump
.SALL
.XCREF BYPASS
BYPASS:
SET_MACRO_EXPANSION
ENDIF ;; IFE BACKWARD_JUMP
ENDM ;; LJMP
;;
;; Conditional jump to TARGET_LABEL. Automatically
;; determine whether backward jump is LONG or SHORT. Set some
;; value in LONG to force a forward long jump. If a forward
;; jump is LONG but could be SHORT, write a remark to that effect
;; and set UNNECESSARY_LONG_COMMENTS to 1.
;; CONDITION is any completion to the J conditional jump prefix
;; except CXZ, which deson't have a negation.
.XCREF LCJMP
LCJMP MACRO CONDITION, TARGET_LABEL, LONG
LOCAL BYPASS
SET_BACKWARD_JUMP TARGET_LABEL ;; 1 if backward, 0 if forward
IF BACKWARD_JUMP
SET_SHORT_BACK TARGET_LABEL ;; SHORT_LABEL is TRUE if SHORT
ELSE ;; Forward
SHORT_LABEL = 1 ;; Assume SHORT
IFNB <LONG> ;; LONG is not blank
.XCREF LCJMP_MAX_SHORT
LCJMP_MAX_SHORT = 127 + OFFSET BYPASS - $
IS_LONG_NECESSARY TARGET_LABEL, LCJMP_MAX_SHORT
;; Write comment for unnecessary LONG
SHORT_LABEL = 0
ENDIF ;; IFNB
ENDIF ;; IF BACKWARD_JUMP
IF SHORT_LABEL
J&CONDITION TARGET_LABEL
ELSE ;; LONG
JN&CONDITION BYPASS
JMP TARGET_LABEL
ENDIF ;; If short label
IFE SHORT_LABEL AND BACKWARD_JUMP
;; Everything but short backward jump
IF SHORT_LABEL ;; Short forward jump
.XCREF BYPASS
.SALL
ENDIF ;; IF SHORT_LABEL
BYPASS:
IF SHORT_LABEL ;; Short forward jump
SET_MACRO_EXPANSION
ENDIF ;; IF SHORT_LABEL
ENDIF ;; IFE SHORT_LABEL AND BACKWARD_JUMP
ENDM ;; LCJMP
;;
;; JCXZ to target label. Automatically determine whether
;; backward jump is LONG or SHORT. Set some value in LONG to force
;; a forward long jump. If a forward jump is LONG but could be
;; SHORT, write a remark to that effect and set
;; UNNECESSARY_LONG_COMMENTS to 1.
.XCREF LJCXZ
LJCXZ MACRO TARGET_LABEL, LONG
LOCAL BYPASS
SET_BACKWARD_JUMP TARGET_LABEL ;; 1 if backward, 0 if forward
IF BACKWARD_JUMP
SET_SHORT_BACK TARGET_LABEL ;; SHORT_LABEL is TRUE if SHORT
ELSE ;; Forward
SHORT_LABEL = 1 ;; Assume SHORT
IFNB <LONG> ;; LONG is not blank
.XCREF LJXCZ_MAX_SHORT
LJXCZ_MAX_SHORT = 127 + OFFSET BYPASS - $
IS_LONG_NECESSARY TARGET_LABEL, LJXCZ_MAX_SHORT
;; Write comment for unnecessary LONG
SHORT_LABEL = 0
ENDIF ;; IFNB
ENDIF ;; IF BACKWARD_JUMP
IF SHORT_LABEL
JCXZ TARGET_LABEL
ELSE ;; LONG
OR CX,CX ;; Is CX zero?
JNZ BYPASS ;; No. Bypass long jump
JMP TARGET_LABEL
ENDIF ;; If short label
IFE SHORT_LABEL AND BACKWARD_JUMP
;; Everything but short backward jump
IF SHORT_LABEL ;; Short forward jump
.XCREF BYPASS
.SALL
ENDIF ;; IF SHORT_LABEL
BYPASS:
IF SHORT_LABEL ;; Short forward jump
SET_MACRO_EXPANSION
ENDIF ;; IF SHORT_LABEL
ENDIF ;; IFE SHORT_LABEL AND BACKWARD_JUMP
ENDM ;; LJCXZ
;;
;; Loop to TARGET_LABEL. Automatically determine whether a
;; backward jump is LONG or SHORT. Set some value in LONG to force
;; a forward long jump. If a forward jump is LONG but could be SHORT,
;; write a remark to that effect and set UNNECESSARY_LONG_COMMENTS to 1.
;; CONDITION is blank for LOOP, E for LOOPE, Z for LOOPZ,
;; NZ for LOOPNZ or NE for LOOPNE.
.XCREF LLOOP
LLOOP MACRO CONDITION, TARGET_LABEL, LONG
LOCAL BYPASS
SET_BACKWARD_JUMP TARGET_LABEL ;; 1 if backward, 0 if forward
IF BACKWARD_JUMP
SET_SHORT_BACK TARGET_LABEL ;; SHORT_LABEL is TRUE if SHORT
ELSE ;; Forward
SHORT_LABEL = 1 ;; Assume SHORT
IFNB <LONG> ;; LONG is not blank
.XCREF LLOOP_MAX_SHORT
LLOOP_MAX_SHORT EQU 127 + OFFSET BYPASS - $
IS_LONG_NECESSARY TARGET_LABEL, LLOOP_MAX_SHORT
;; Write comment for unnecessary LONG
SHORT_LABEL = 0
ENDIF ;; IFNB
ENDIF ;; IF BACKWARD_JUMP
IF SHORT_LABEL
LOOP&CONDITION TARGET_LABEL
ELSE ;; LONG
IFNB <CONDITION> ;; Don't test unconditional loop
JN&CONDITION BYPASS ;; Terminate loop if not CONDITION
ENDIF ;; IFNB CONDITION
DEC CX ;; Does CX become zero
JZ BYPASS ;; Yes. Terminate loop
JMP TARGET_LABEL
ENDIF ;; If short label
IFE SHORT_LABEL AND BACKWARD_JUMP
;; Everything but short backward jump
IF SHORT_LABEL ;; Short forward jump
.XCREF BYPASS
.SALL
ENDIF ;; IF SHORT_LABEL
BYPASS:
IF SHORT_LABEL ;; Short forward jump
SET_MACRO_EXPANSION
ENDIF ;; IF SHORT_LABEL
ENDIF ;; IFE SHORT_LABEL AND BACKWARD_JUMP
ENDM ;; LLOOP
;;
;;
;; From here on, the macros are more complicated. They
;; use automatic labels, whose names are sequentially indexed as
;; L?1, L?2, ... Macro variable L?CNT contains the last index used,
;; and the next label will be L?&(L?CNT+1). They also use a macro
;; stack, whose members are indexec as S?1, S?2, ... Each Sn contains a
;; number which the macros use to construct a L?n label. Macro
;; variable S?CNT contains the S? index of the top of the macro stack,
;; so a push will increment S?CNT, and a pop will decrement it.
;;
;; Mark Hersey named the macro variables consistently as
;; follows:
;; ENTRY The index of a macro stack variable.
;; NUMBER The index of a label or a macro stack
;; variable.
;; CONDITION The condition part of a jump instruction,
;; i.e., JZ would have CONDITION Z.
;; LONG This is always an optional variable. If
;; it exists, it forces a LONG jump of some
;; sort. If not, it forces a SHORT jump.
;; VAR1 The required first variable of a compare.
;; VAR2 The optional second variable of a compare.
;; If it exists, it forces a CMP instruction.
;; If it doesn't, the macro uses an
;; AND VAR1,VAR1 instruction, which lets you
;; test for zero or a negative variable.
;; To simplify the descriptions below, I will use the following
;; terminology:
;; L?N is a label with index N.
;; CURRENT is L?CNT.
;; NEXT is L?CNT+1.
;; SECOND is L?CNT+2.
;; S?N is the contents of macro stack variable with index N.
;; stacktop is the contents of S?(S?CNT), the top of the stack.
;; stacknext is (stacktop + 1).
;;
;; Add 1 to contents of LABEL. Written by LP, with
;; modifications to later macros which had this operation.
;;
;; Initialize the label and macro stack indices to 0.
.XCREF XINIT
XINIT MACRO
.XCREF L?CNT,S?CNT
L?CNT = 0
S?CNT = 0
ENDM
;;
;; Initialize the label and macro stack indices to 0 if necessary
.XCREF PHASE_1_XINIT, PHASE_2_XINIT, XINIT_IF_NECESSARY
PHASE_1_XINIT = 1
PHASE_2_XINIT = 1
XINIT_IF_NECESSARY MACRO
IF1
IF PHASE_1_XINIT ;; Not initialized
XINIT
PHASE_1_XINIT = 0
ENDIF ;; IF PHASE_1_XINIT
ENDIF ;; IF1
IF2
IF PHASE_2_XINIT ;; Not re-initialized
XINIT
PHASE_2_XINIT = 0
ENDIF ;; IF PHASE_2_XINIT
ENDIF ;; IF2
ENDM ;; XINIT_IF_NECESSARY
;;
.XCREF XINC
XINC MACRO LABEL
XINIT_IF_NECESSARY
LABEL = LABEL+1
ENDM ;; XINC
;;
.XCREF XSAVE,XPUSH,XPOP
;;
;; Set macro stack variable with index ENTRY to label index
;; NUMBER. In these macros, ENTRY is always S?CNT, and NUMBER is
;; always L?CNT, so the macro becomes, "Set stacktop to the current
;; label index."
XSAVE MACRO ENTRY,NUMBER
.XCREF S?&ENTRY
S?&ENTRY= NUMBER
ENDM
;;
;; Push the next label index on the macro stack.
;; Recall that XINC invokes XINIT_IF_NECESSARY.
XPUSH MACRO
XINC L?CNT ;; Set CURRENT to NEXT
XINC S?CNT ;; Set stacktop to stacknext
XSAVE %S?CNT,%L?CNT
ENDM
;;
;; Pop the macro stack.
;; No automatic initialization to force an error if no XPUSH first.
XPOP MACRO
S?CNT = S?CNT-1
ENDM
;;
.XCREF XLBL,XLBLS,XLBLI
;;
XLBL MACRO NUMBER
.XCREF L?&NUMBER
L?&NUMBER:
ENDM
;;
;; Write the label L?(S?NUMBER):. In these macros, NUMBER is
;; always S?CNT, so the macro becomes, "Write the label L?stacktop."
XLBLS MACRO NUMBER
XLBL %S?&NUMBER
ENDM
;;
;; Write the label L?(stacktop+1).
XLBLI MACRO NUMBER
XINIT_IF_NECESSARY
XLBL %(S?&NUMBER&+1)
ENDM
;;
.XCREF XJMP,XJMPS,XJMPI
;;
;; Jump to L?NUMBER.
XJMP MACRO NUMBER,LONG
LJMP L?&NUMBER, LONG
ENDM
;;
;; Jump to L?(S?NUMBER).
XJMPS MACRO NUMBER,LONG
XJMP %S?&NUMBER,LONG
ENDM
;;
;; Jump to L?(S?NUMBER+1).
XJMPI MACRO NUMBER,LONG
XJMP %(S?&NUMBER+1),LONG
ENDM
;;
.XCREF XLP,XLPS
;;
;; LOOP for CONDITION to L?NUMBER.
XLP MACRO CONDITION,NUMBER,LONG
LLOOP CONDITION,L?&NUMBER,LONG
ENDM
;;
;; LOOP for CONDITION to L?(S?NUMBER). In these macros,
;; S?NUMBER is always S?CNT, so the macro becomes, "LOOP for CONDITION
;; to L?stacktop."
XLPS MACRO CONDITION,NUMBER,LONG
XLP CONDITION,%S?&NUMBER,LONG
ENDM
;;
.XCREF XCJMP,XCJMPS,XCJMPI,XCXZJMP
;;
;; Jump on condition to L?NUMBER.
XCJMP MACRO CONDITION,NUMBER,LONG
LCJMP CONDITION,L?&NUMBER,LONG
ENDM
;;
;; Jump on CONDITION to L?(S?NUMBER).
XCJMPS MACRO CONDITION,NUMBER,LONG
XCJMP CONDITION,%S?&NUMBER,LONG
ENDM
;;
;; Jump on CONDITION to L?(S?NUMBER+1).
XCJMPI MACRO CONDITION,NUMBER,LONG
XINIT_IF_NECESSARY
XCJMP CONDITION,%(S?&NUMBER+1),LONG
ENDM
;;
;; Jump if CX = 0 to label L?NUMBER.
XCXZJMP MACRO NUMBER,LONG
LJCXZ L?&NUMBER,LONG
ENDM
;;
.XCREF XIFC,XANDIFC,COMPARE,XIF,XANDIF,XELSE,XENDIF
;;
;; Push the next label index on the macro stack.
;; Jump if CONDITION is false to L?stacktop.
;; NOTE that the jump is apparently backward to allow for ANDing
;; conditions with XANDIFC.
XIFC MACRO CONDITION,LONG
XPUSH
XCJMP N&CONDITION,%L?CNT,LONG
ENDM
;;
;; Jump if CONDITION is false to L?stacktop.
;; Use only after invoking XIFC to set up the macro stack.
XANDIFC MACRO CONDITION,LONG
XCJMP N&CONDITION,%L?CNT,LONG
ENDM
;;
;; Compare VAL1 to either itself or VAL2.
COMPARE MACRO VAL1,VAL2
IFNB <VAL2>
CMP VAL1,VAL2
ENDIF
IFB <VAL2>
AND VAL1,VAL1
ENDIF
ENDM
;;
;; Compare VAL1 to either itself or VAL2.
;; Jump if CONDITION is false to L?stacktop.
;; NOTE that the jump is apparently backward to allow for ANDing
;; conditions with XANDIFC.
XIF MACRO VAL1,CONDITION,VAL2,LONG
COMPARE <VAL1>,<VAL2>
XIFC CONDITION,LONG
ENDM
;;
;; Compare VAL1 to either itself or VAL2.
;; Jump if CONDITION is false to L?stacktop.
;; Use only after invoking XIFC to set up the macro stack.
XANDIF MACRO VAL1,CONDITION,VAL2,LONG
COMPARE <VAL1>,<VAL2>
XANDIFC CONDITION,LONG
ENDM
;;
;; Increment CURRENT and jump to L?CURRENT.
;; Write a l?stacktop here as a label
;; Replace stacktop with CURRENT.
XELSE MACRO LONG
XINC L?CNT ;; Set CURRENT to NEXT
XJMP %L?CNT,LONG
XLBLS %S?CNT
XSAVE %S?CNT,%L?CNT
ENDM
;;
;; Write L?stacktop here as a label.
;; Pop the macro stack.
XENDIF MACRO
XLBLS %S?CNT
XPOP
ENDM
;;
.XCREF XLOOP,XENDLP,XBEGIN,XEND,XNEXT,XCNEXTC,XCNEXT,XCEXITC,XEXIT
.XCREF XCEXIT,XWHILEC,XWHILE,XREPEAT,XUNTILC,XUNTIL,XFOR,XDEC
;;
;; Increment CURRENT and push it on the macro stack
;; Write L?CURRENT here as a label
;; Increment CURRENT again, so that it is the original SECOND
XLOOP MACRO
XPUSH
XLBL %L?CNT
XINC L?CNT ;; Set CURRENT to NEXT
ENDM
;;
;; Increment CURRENT and push it on the macro stack
;; Write L?CURRENT as a label.
XBEGIN MACRO
XPUSH
XLBL %L?CNT
XINC L?CNT ;; Set CURRENT to NEXT
ENDM
;;
;; Jump on CONDITION to stacktop
XNEXT MACRO LONG
XJMPS %S?CNT,LONG
ENDM
;;
;; Jump on CONDITION to stacktop
XCNEXTC MACRO CONDITION,LONG
XCJMPS CONDITION,%S?CNT,LONG
ENDM
;;
;; Compare VAL1 to VAL2 or itself
;; Jump on CONDITION to stacktop
XCNEXT MACRO VAL1,CONDITION,VAL2,LONG
COMPARE <VAL1>,<VAL2>
XCNEXTC CONDITION,LONG
ENDM
;;
;; Jump to stacknext.
XEXIT MACRO LONG
XJMPI %S?CNT,LONG
ENDM
;;
;; Jump on condition to stacknext.
XCEXITC MACRO CONDITION,LONG
XCJMPI CONDITION,%S?CNT,LONG
ENDM
;;
;; Compare VAL1 with either itself or VAL2
;; Jump on condition to stacknext
XCEXIT MACRO VAL1,CONDITION,VAL2,LONG
COMPARE <VAL1>,<VAL2>
XCEXITC CONDITION,LONG
ENDM
;;
;; Jump to stacktop
;; Write the label L?stacknext
XENDLP MACRO LONG
XJMPS %S?CNT,LONG
XLBLI %S?CNT
XPOP
ENDM
;;
;; Write the label L?stacknext
;; Pop the macro stack
XEND MACRO
XLBLI %S?CNT
XPOP
ENDM
;;
;; Increment CURRENT and push it on the macro stack
;; Write L?CURRENT here as a label
;; Increment CURRENT again, so that it is the original SECOND
;; Jump on CONDITION FALSE to stacknext, which is now CURRENT
XWHILEC MACRO CONDITION,LONG
XLOOP
XCEXITC N&CONDITION,LONG
ENDM
;;
;; Increment CURRENT and push it on the stack
;; Write L?CURRENT here as a label
;; Increment CURRENT again, so that it is the original SECOND
;; Compare VAL1 with either itself or VAL2
;; Jump on CONDITION FALSE to stacknext, which is now CURRENT
XWHILE MACRO VAL1,CONDITION,VAL2,LONG
XLOOP
XCEXIT <VAL1>,N&CONDITION,<VAL2>,LONG
ENDM
;;
;; Increment CURRENT and push it on the stack
;; Write L?CURRENT here as a label
;; Increment CURRENT again, so that it is the original SECOND
XREPEAT MACRO
XLOOP
ENDM
;;
;; Jump on CONDITION FALSE to stacknext, which is now CURRENT
;; Write L?NEXT here as a label
;; Pop the macro stack
XUNTILC MACRO CONDITION
XCJMPS N&CONDITION,%S?CNT
XLBLI %S?CNT
XPOP
ENDM
;;
;; Compare VAL1 with either itself or VAL2
;; Jump on CONDITION FALSE to stacknext, which is now CURRENT
;; Write L?NEXT here as a label
;; Pop the macro stack
XUNTIL MACRO VAL1,CONDITION,VAL2
COMPARE <VAL1>,<VAL2>
XUNTILC CONDITION
ENDM
;;
;; If no arguments, forward jump to L?SECOND
;; Increment L?CNT and push it
;; Write L?CURRENT here as a label
;; Increment L?CURREMT again, so it is now the original SECOND
XFOR MACRO NOZERO,LONG
;; NOZERO's value doesn't matter. Any value will surpress the forward
;; jump.
IFB <NOZERO>
XINIT_IF_NECESSARY
XCXZJMP %(L?CNT+2),LONG
ENDIF
XLOOP
ENDM
;;
;; LOOP on CONDITION to L?stacktop
;; Write the label L?(stacktop+1).
;; POP the macro stack
XDEC MACRO CONDITION,LONG
XLPS CONDITION,%S?CNT,LONG
XLBLI %S?CNT
XPOP
ENDM
;;
.XCREF CALLSTR,FCBCALL,GETCALL
;;
;; In the next three macros,
;; ROUTINE is the routine name.
;; STRING is the name of the variable which BX or DX is to point to.
;;
;; Call a routine which uses an offset in DX
CALLSTR MACRO ROUTINE,STRING
MOV DX,OFFSET STRING
CALL ROUTINE
ENDM
;;
;; Call a routine which uses an offset in BX
GETCALL MACRO ROUTINE,STRING
MOV BX,OFFSET STRING
CALL ROUTINE
ENDM
;;
;; Call a routine which calls a DOS file control block function.
;; The routine should reset the zero flag if it succeeds and set
;; the zero flag for failure.
;;
FCBCALL MACRO ROUTINE,FCB,ERRORRTN
;; ERRORRTN is an optional label for an error routine.
MOV DX,OFFSET FCB
CALL ROUTINE
IFNB <ERRORRTN>
JZ ERRORRTN
ENDIF
ENDM
;;
.CREF
SET_MACRO_EXPANSION
;;